package com.desktopmng.core.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;


/**
 * 扩展Apache Commons BeanUtils, 提供一些反射方面缺失功能的封装.
 * @author Administrator
 *
 */
public class BeanUtils extends org.apache.commons.beanutils.BeanUtils {
	protected static final Log logger = LogFactory.getLog(BeanUtils.class);

	private BeanUtils() {
	}

	/**
	 * 循环向上转型,获取对象的DeclaredField.
	 *
	 * @throws NoSuchFieldException 如果没有该Field时抛出.
	 */
	public static Field getDeclaredField(Object object, String propertyName) throws NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);
		return getDeclaredField(object.getClass(), propertyName);
	}

	/**
	 * 循环向上转型,获取对象的DeclaredField.
	 *
	 * @throws NoSuchFieldException 如果没有该Field时抛出.
	 */
	public static Field getDeclaredField(Class clazz, String propertyName) throws NoSuchFieldException {
		Assert.notNull(clazz);
		Assert.hasText(propertyName);
		for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(propertyName);
			} catch (NoSuchFieldException e) {
				// Field不在当前类定义,继续向上转型
			}
		}
		throw new NoSuchFieldException("No such field: " + clazz.getName() + '.' + propertyName);
	}

	/**
	 * 暴力获取对象变量值,忽略private,protected修饰符的限制.
	 *
	 * @throws NoSuchFieldException 如果没有该Field时抛出.
	 */
	public static Object forceGetProperty(Object object, String propertyName) throws NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);

		Field field = getDeclaredField(object, propertyName);

		boolean accessible = field.isAccessible();
		field.setAccessible(true);

		Object result = null;
		try {
			result = field.get(object);
		} catch (IllegalAccessException e) {
			logger.info("error wont' happen");
		}
		field.setAccessible(accessible);
		return result;
	}

	/**
	 * 暴力设置对象变量值,忽略private,protected修饰符的限制.
	 *
	 * @throws NoSuchFieldException 如果没有该Field时抛出.
	 */
	public static void forceSetProperty(Object object, String propertyName, Object newValue)
			throws NoSuchFieldException {
		Assert.notNull(object);
		Assert.hasText(propertyName);

		Field field = getDeclaredField(object, propertyName);
		boolean accessible = field.isAccessible();
		field.setAccessible(true);
		try {
			field.set(object, newValue);
		} catch (IllegalAccessException e) {
			logger.info("Error won't happen");
		}
		field.setAccessible(accessible);
	}

	/**
	 * 暴力调用对象函数,忽略private,protected修饰符的限制.
	 *
	 * @throws NoSuchMethodException 如果没有该Method时抛出.
	 */
	public static Object invokePrivateMethod(Object object, String methodName, Object... params)
			throws NoSuchMethodException {
		Assert.notNull(object);
		Assert.hasText(methodName);
		Class[] types = new Class[params.length];
		for (int i = 0; i < params.length; i++) {
			types[i] = params[i].getClass();
		}

		Class clazz = object.getClass();
		Method method = null;
		for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				method = superClass.getDeclaredMethod(methodName, types);
				break;
			} catch (NoSuchMethodException e) {
				// 方法不在当前类定义,继续向上转型
			}
		}

		if (method == null)
			throw new NoSuchMethodException("No Such Method:" + clazz.getSimpleName() + methodName);

		boolean accessible = method.isAccessible();
		method.setAccessible(true);
		Object result = null;
		try {
			result = method.invoke(object, params);
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}
		method.setAccessible(accessible);
		return result;
	}

	/**
	 * 按Filed的类型取得Field列表.
	 */
	public static List<Field> getFieldsByType(Object object, Class type) {
		List<Field> list = new ArrayList<Field>();
		Field[] fields = object.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (field.getType().isAssignableFrom(type)) {
				list.add(field);
			}
		}
		return list;
	}

	/**
	 * 按FiledName获得Field的类型.
	 */
	public static Class getPropertyType(Class type, String name) throws NoSuchFieldException {
		return getDeclaredField(type, name).getType();
	}

	/**
	 * 获得field的getter函数名称.
	 */
	public static String getGetterName(Class type, String fieldName) {
		Assert.notNull(type, "Type required");
		Assert.hasText(fieldName, "FieldName required");

		if (type.getName().equals("boolean")) {
			return "is" + StringUtils.capitalize(fieldName);
		} else {
			return "get" + StringUtils.capitalize(fieldName);
		}
	}

	/**
	 * 获得field的getter函数,如果找不到该方法,返回null.
	 */
	public static Method getGetterMethod(Class type, String fieldName) {
		try {
			return type.getMethod(getGetterName(type, fieldName));
		} catch (NoSuchMethodException e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}
	
	//add by C.Cheng
	/**
	 * 按Filed的类型取得指定对象的public属性实例列表
	 * @param obj 指定的对象
	 * @param type 指定的field类型，只有类型是type本身或者继承(实现)type的field才算符合要求
	 * @return 符合条件的field实例列表，如果没有符合条件的field实例，则返回size为0的列表
	 * @throws IllegalArgumentException 当参数中有null值时抛出该异常
	 */
	public static <T> List<T> getPublicFieldsValueByType(Object obj, Class<T> type){
		return getFieldsValueByType(obj,type,false);
	}
	
	/**
	 * 按Filed的类型取得指定对象的属性实例列表，可以指定是否无视该field的访问控制级别.
	 * @param obj 指定的对象
	 * @param type 指定的field类型，只有类型是type本身或者继承(实现)type的field才算符合要求
	 * @param surly true则为无视field的访问控制级别，false则只取公共的属性
	 * @return 符合条件的field实例列表，如果没有符合条件的field实例，则返回size为0的列表
	 * @throws IllegalArgumentException 当参数中有null值时抛出该异常
	 */
	public static <T> List<T> getFieldsValueByType(Object obj, Class<T> type,boolean surly){
		Assert.notNull(obj, "obj required");
		Assert.notNull(type, "type required");
		List<T> result = new ArrayList<T>();
		Class<?> objtype = obj.getClass();
		Field[] fields = objtype.getDeclaredFields();
		for (Field field : fields) {
			if (type.isAssignableFrom(field.getType())) {
				Object tempObj = null;
				if(surly){
					tempObj = getFieldValuesSurly(obj,field);
				}else{
					tempObj = getFieldValuesTender(obj,field);
				}
				if(tempObj!=null){
					result.add(type.cast(tempObj));
				}
			}
		}
		return result;
	}
	
	/**
	 * 在指定对象中取得具有指定类型参数以及指定类型的Public field实例
	 * @param obj 指定的对象
	 * @param type 指定的field类型，只有类型是type本身或者继承(实现)type的field才算符合要求
	 * @param parameterTypes 指定的类型参数数组
	 * @return 符合条件的field实例列表，如果没有符合条件的field实例，则返回size为0的列表
	 * @throws IllegalArgumentException 当参数中有null值时抛出该异常
	 */
	public static <T> List<T> getPublicFieldsValueByTypeAndParameterType(Object obj, Class<T> type,Class<?>[] parameterTypes){
		return getFieldsValueByTypeAndParameterType(obj,type,parameterTypes,false);
	}
	
	/**
	 * 在指定对象中取得具有指定类型参数以及指定类型的field实例，可以指定是否无视该field的访问控制级别.
	 * @param obj 指定的对象
	 * @param type 指定的field类型，只有类型是type本身或者继承(实现)type的field才算符合要求
	 * @param parameterTypes 指定的类型参数数组
	 * @param surly true则为无视field的访问控制级别，false则只取公共的属性
	 * @return 符合条件的field实例列表，如果没有符合条件的field实例，则返回size为0的列表
	 * @throws IllegalArgumentException 当参数中有null值时抛出该异常
	 */
	public static <T> List<T> getFieldsValueByTypeAndParameterType(Object obj, Class<T> type,Class<?>[] parameterTypes,boolean surly){
		Assert.notNull(obj, "obj required");
		Assert.notNull(type, "type required");
		Assert.notNull(type, "parameterTypes required");
		List<T> result = new ArrayList<T>();
		Class<?> objtype = obj.getClass();
		Field[] fields = objtype.getDeclaredFields();
		for (Field field : fields) {
			if (type.isAssignableFrom(field.getType())) {
				Type fieldGenericType = field.getGenericType();
				if(fieldGenericType instanceof ParameterizedType){
					Type[] actParameterTypes = ((ParameterizedType)fieldGenericType).getActualTypeArguments();
					if(actParameterTypes.length==parameterTypes.length){
						boolean isSuited = true;
						for(int i=0;i<actParameterTypes.length;i++){
							if(!(actParameterTypes[i] instanceof Class<?>)){
								isSuited = false;
								break;
							}
							Class<?> tempClass = (Class<?>) actParameterTypes[i];
							if(parameterTypes[i].isAssignableFrom(tempClass)){
								continue;
							}else{
								isSuited = false;
								break;
							}
						}
						if(isSuited){
							if(surly){
								result.add(type.cast(getFieldValuesSurly(obj,field)));
							}else{
								result.add(type.cast(getFieldValuesTender(obj,field)));
							}
						}
					}
				}
			}
		}
		return result;
	}
	
	/**
	 * 按照sun提出的getter、setter命名规范，根据给出的fieldName提供符合规范的getter、setter中的fieldName
	 * @param fieldName 给出的类属性名
	 * @return 转化后的属性名
	 */
	private static String fieldNameInGetterMethod(String fieldName){
		if(fieldName.length()==1){
			return StringUtils.capitalize(fieldName);
		}else if(Character.isUpperCase(fieldName.charAt(1))){
			return fieldName;
		}else{
			return StringUtils.capitalize(fieldName);
		}
	}
	
	/**
	 * 无视属性的访问控制级别，获取指定对象中的指定field实例
	 * @param obj 指定的对象
	 * @param field 指定的属性
	 * @return 符合条件的field实例，如果没有则返回null
	 * @throws IllegalArgumentException 当参数中有null值时抛出该异常
	 */
	public static Object getFieldValuesSurly(Object obj,Field field){
		Assert.notNull(obj, "obj required");
		Assert.notNull(field, "field required");
		boolean accessible = field.isAccessible();
		try{
			field.setAccessible(true);
			return field.get(obj);
		}catch (IllegalArgumentException e) {
			logger.error(null, e);
			return null;
		} catch (IllegalAccessException e) {
			logger.error(null, e);
			return null;
		}finally{
			field.setAccessible(accessible);
		}
	}
	
	/**
	 * 获取指定对象中的指定public field实例
	 * @param obj 指定的对象
	 * @param field 指定的属性
	 * @return 符合条件的field实例，如果没有则返回null
	 * @throws IllegalArgumentException 当参数中有null值时抛出该异常
	 */
	public static Object getFieldValuesTender(Object obj,Field field){
		Assert.notNull(obj, "obj required");
		Assert.notNull(field, "field required");
		if(Modifier.isPublic(field.getModifiers())){
			try {
				return field.get(obj);
			} catch (IllegalArgumentException e) {
				logger.error(null, e);
				return null;
			} catch (IllegalAccessException e) {
				logger.error(null, e);
				return null;
			}
		}else{
			Class<?> type = field.getType();
			Class<?> objtype = obj.getClass();
			String methodNamePrefix = null;
			if(type.getName().equals("boolean")){
				methodNamePrefix = "is";
			}else{
				methodNamePrefix = "get";
			}
			try {
				Method method = objtype.getMethod(methodNamePrefix
						+fieldNameInGetterMethod(field.getName()));
				return method.invoke(obj);
			} catch (SecurityException e) {
				logger.error(null, e);
				return null;
			} catch (NoSuchMethodException e) {
				logger.error(null, e);
				return null;
			} catch (InvocationTargetException e) {
				logger.error(null, e);
				return null;
			} catch (IllegalArgumentException e) {
				logger.error(null, e);
				return null;
			} catch (IllegalAccessException e) {
				logger.error(null, e);
				return null;
			}
		}
	}
	
	/**
	 * 无视属性的访问控制级别，为指定对象中的指定field设置值
	 * @param obj 指定的对象
	 * @param field 指定的属性
	 * @param value 要设置的值
	 * @throws IllegalArgumentException 当obj或者field为null时抛出该异常
	 */
	public static void setFieldValuesSurly(Object obj,Field field,Object value){
		Assert.notNull(obj, "obj required");
		Assert.notNull(field, "field required");
		boolean accessible = field.isAccessible();
		try{
			field.setAccessible(true);
			field.set(obj, value);
		}catch (IllegalArgumentException e) {
			logger.error(null, e);
		} catch (IllegalAccessException e) {
			logger.error(null, e);
		}finally{
			field.setAccessible(accessible);
		}
	}
	
	/**
	 * 为指定对象中的指定public field设置值
	 * @param obj 指定的对象
	 * @param field 指定的属性
	 * @param value 要设置的值
	 * @throws IllegalArgumentException 当obj或者field为null时抛出该异常
	 */
	public static void setFieldValuesTender(Object obj,Field field,Object value){
		Assert.notNull(obj, "obj required");
		Assert.notNull(field, "field required");
		if(Modifier.isPublic(field.getModifiers())){
			try {
				field.set(obj, value);
			} catch (IllegalArgumentException e) {
				logger.error(null, e);
			} catch (IllegalAccessException e) {
				logger.error(null, e);
			}
		}else{
			Class<?> type = field.getType();
			Class<?> objtype = obj.getClass();
			String methodNamePrefix = "set";
			try {
				Method method = objtype.getMethod(methodNamePrefix
						+fieldNameInGetterMethod(field.getName()),type);
				method.invoke(obj, value);
			} catch (SecurityException e) {
				logger.error(null, e);
			} catch (NoSuchMethodException e) {
				logger.error(null, e);
			} catch (InvocationTargetException e) {
				logger.error(null, e);
			} catch (IllegalArgumentException e) {
				logger.error(null, e);
			} catch (IllegalAccessException e) {
				logger.error(null, e);
			}
		}
	}
	
	/**
	 * 获取指定类中具有指定注解的field列表
	 * @param clazz 指定的对象
	 * @param annotation 指定的注解
	 * @return 符合条件的field列表，如果没有则返回size为0的列表
	 * @throws IllegalArgumentException 当参数中有null值时抛出该异常
	 */
	public static <T extends Annotation> List<Field> getFieldsByPresentAnnotation(Class<?> clazz,Class<T> annotation){
		Assert.notNull(clazz, "clazz required");
		Assert.notNull(annotation, "annotation required");
		List<Field> result = new ArrayList<Field>();
		Field fields[] = clazz.getFields();
		for(Field field:fields){
			if(field.isAnnotationPresent(annotation)){
				result.add(field);
			}
		}
		return result;
	}
	
	/**
	 * 将指定的字符串赋值于指定对象中的值为null的字符串属性
	 * @param bean 要赋值的对象实例
	 * @param replaceMent 指定的字符串
	 * @return 赋值后的对象实例
	 */
	public static Object nvlStringFields(Object bean,String replaceMent){
		if(bean!=null){
			Class<?> clazz = bean.getClass();
			do{
				Field fields[] = clazz.getDeclaredFields();
				for(Field field:fields){
					if(field.getType().isAssignableFrom(String.class)){
						Object value = getFieldValuesTender(bean,field);
						if(value==null){
							setFieldValuesTender(bean,field,replaceMent);
						}
					}
				}
			}while((clazz=clazz.getSuperclass())!=null);
		}
		return bean;
	}
}
